﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Media.Media3D;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using Eniux.Windows.ThreeDEngine.Helper;
using Eniux.Windows.ThreeDEngine.Geometry;
using Eniux.Windows.ThreeDEngine.AttachObject;

namespace Eniux.Windows.ThreeDEngine.Base.CameraControllers
{
    /// <summary>
    /// 描述鼠标手势的抽象类
    /// </summary>
    internal abstract class MouseGestureHandler
    {
        #region 构造、析构

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="controller">相机控制器</param>
        protected MouseGestureHandler(CameraController controller)
        {
            this.Controller = controller;
            this.ManipulationWatch = new Stopwatch();
        }

        #endregion

        #region 公共属性

        /// <summary>
        ///  源点
        /// </summary>
        public Point3D Origin
        {
            get
            {
                if (this.MouseDownNearestPoint3D != null)
                {
                    return this.MouseDownNearestPoint3D.Value;
                }

                if (this.MouseDownPoint3D != null)
                {
                    return this.MouseDownPoint3D.Value;
                }

                return new Point3D();
            }
        }

        #endregion

        #region 属性

        /// <summary>
        /// 相机
        /// </summary>
        protected ProjectionCamera Camera
        {
            get
            {
                return this.Viewport.Camera as ProjectionCamera;
            }
        }

        /// <summary>
        /// 获取或设置相机拍摄方向
        /// </summary>
        protected Vector3D CameraLookDirection
        {
            get
            {
                return this.Controller.CameraLookDirection;
            }

            set
            {
                this.Controller.CameraLookDirection = value;
            }
        }

        /// <summary>
        /// 设置相机模式
        /// </summary>
        protected CameraMode CameraMode
        {
            get
            {
                return AttachCamera.GetCameraMode(Controller);
            }
        }

        /// <summary>
        /// 获取或设置相机位置
        /// </summary>
        protected Point3D CameraPosition
        {
            get
            {
                return this.Controller.CameraPosition;
            }

            set
            {
                this.Controller.CameraPosition = value;
            }
        }

        /// <summary>
        /// 获取或设置相机拍摄目标点
        /// </summary>
        protected Point3D CameraTarget
        {
            get
            {
                return this.CameraPosition + this.CameraLookDirection;
            }
        }

        /// <summary>
        /// 获取或设置相机向上方向
        /// </summary>
        protected Vector3D CameraUpDirection
        {
            get
            {
                return this.Controller.CameraUpDirection;
            }

            set
            {
                this.Controller.CameraUpDirection = value;
            }
        }

        /// <summary>
        /// 获取或设置相机控制器
        /// </summary>
        protected CameraController Controller { get; set; }

        /// <summary>
        /// 获取或设置最后的2D点
        /// </summary>
        protected Point LastPoint { get; set; }

        /// <summary>
        /// 获取或设置最后的3D点
        /// </summary>
        protected Point3D? LastPoint3D { get; set; }

        /// <summary>
        /// 获取或设置操作的表。
        /// </summary>
        protected Stopwatch ManipulationWatch { get; set; }

        /// <summary>
        /// 获取或设置模型向上方向
        /// </summary>
        protected Vector3D ModelUpDirection
        {
            get
            {
                return AttachCamera.GetModelUpDirection(Controller);
            }
        }

        /// <summary>
        ///  获取或设置最近的3D点
        /// </summary>
        protected Point3D? MouseDownNearestPoint3D { get; set; }

        /// <summary>
        /// 获取或设置鼠标按下的2D点
        /// </summary>
        protected Point MouseDownPoint { get; set; }

        /// <summary>
        /// 获取或设置鼠标按下的3D点
        /// </summary>
        protected Point3D? MouseDownPoint3D { get; set; }

        /// <summary>
        /// 获取旋转度
        /// </summary>
        protected double RotationSensitivity
        {
            get
            {
                return AttachCamera.GetRotationSensitivity(Controller);
            }
        }

        /// <summary>
        /// 获取3D视窗
        /// </summary>
        protected Viewport3D Viewport
        {
            get
            {
                return this.Controller.Viewport;
            }
        }

        /// <summary>
        /// 获取3D视窗的高度
        /// </summary>
        protected double ViewportHeight
        {
            get
            {
                return this.Controller.ActualHeight;
            }
        }

        /// <summary>
        /// 获取3D视窗的宽度
        /// </summary>
        protected double ViewportWidth
        {
            get
            {
                return this.Controller.ActualWidth;
            }
        }

        /// <summary>
        /// 获取缩放度
        /// </summary>
        protected double ZoomSensitivity
        {
            get
            {
                return AttachCamera.GetZoomSensitivity(Controller);
            }
        }

        /// <summary>
        /// 获取或设置旧的光标
        /// </summary>
        private Cursor OldCursor { get; set; }

        #endregion

        #region 公共方法

        /// <summary>
        /// 当操作完成时，引发该事件
        /// </summary>
        /// <param name="e">操作事件参数</param>
        public virtual void Completed(ManipulationEventArgs e)
        {
            var elapsed = this.ManipulationWatch.ElapsedMilliseconds;
            if (elapsed > 0 && elapsed < this.Controller.SpinReleaseTime)
            {
                this.OnInertiaStarting((int)this.ManipulationWatch.ElapsedMilliseconds);
            }
        }

        /// <summary>
        /// 在操作期间，位置点发生改变时，引发该事件
        /// </summary>
        /// <param name="e">操作事件参数</param>
        public virtual void Delta(ManipulationEventArgs e)
        {
        }

        /// <summary>
        /// 执行鼠标手势操作
        /// </summary>
        /// <param name="sender">触发对象</param>
        /// <param name="e">为路由事件提供参数</param>
        public void Execute(object sender, ExecutedRoutedEventArgs e)
        {
            if (!this.CanExecute())
            {
                return;
            }

            this.Controller.PushCameraSetting();
            this.Controller.MouseMove += this.OnMouseMove;
            this.Controller.MouseUp += this.OnMouseUp;
            this.OnMouseDown(sender, null);
            this.Controller.Focus();
            this.Controller.CaptureMouse();
            this.Controller.PushCameraSetting();
        }

        /// <summary>
        /// 当操作started时，引发该事件
        /// </summary>
        /// <param name="e"></param>
        public virtual void Started(ManipulationEventArgs e)
        {
            this.SetMouseDownPoint(e.CurrentPosition);
            this.LastPoint = this.MouseDownPoint;
            this.LastPoint3D = this.MouseDownPoint3D;
            this.ManipulationWatch.Restart();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p"></param>
        /// <param name="position"></param>
        /// <param name="normal"></param>
        /// <returns></returns>
        public Point3D? UnProject(Point p, Point3D position, Vector3D normal)
        {
            Ray3D ray = this.GetRay(p);
            if (ray == null)
            {
                return null;
            }

            return ray.PlaneIntersection(position, normal);
        }

         /// <summary>
         /// 
         /// </summary>
         /// <param name="p"></param>
         /// <returns></returns>
        public Point3D? UnProject(Point p)
        {
            return this.UnProject(p, this.CameraTarget, this.CameraLookDirection);
        }

        #endregion

        #region 方法

        /// <summary>
        /// 操作是否执行
        /// </summary>
        /// <returns></returns>
        protected virtual bool CanExecute()
        {
            return true;
        }

        /// <summary>
        /// 获取当前手势的光标
        /// </summary>
        /// <returns></returns>
        protected abstract Cursor GetCursor();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        protected Ray3D GetRay(Point position)
        {
            Point3D point1, point2;
            bool ok = Viewport3DHelper.Point2DtoPoint3D(this.Viewport, position, out point1, out point2);
            if (!ok)
            {
                return null;
            }

            return new Ray3D { Origin = point1, Direction = point2 - point1 };
        }

        /// <summary>
        /// 当惯性开始时，引发该事件
        /// </summary>
        /// <param name="elapsedTime"></param>
        protected virtual void OnInertiaStarting(int elapsedTime)
        {
        }

        /// <summary>
        /// 当鼠标按下时，引发该事件
        /// </summary>
        /// <param name="sender">对象</param>
        /// <param name="e">路由参数</param>
        protected virtual void OnMouseDown(object sender, MouseEventArgs e)
        {
            this.Started(new ManipulationEventArgs(Mouse.GetPosition(this.Controller)));

            this.OldCursor = this.Controller.Cursor;
            this.Controller.Cursor = this.GetCursor();
        }

        /// <summary>
        /// 当鼠标移动时，引发该事件
        /// </summary>
        /// <param name="sender">对象</param>
        /// <param name="e">路由参数</param>
        protected virtual void OnMouseMove(object sender, MouseEventArgs e)
        {
            this.Delta(new ManipulationEventArgs(Mouse.GetPosition(this.Controller)));
        }

        /// <summary>
        /// 当鼠标弹起时，引发该事件
        /// </summary>
        /// <param name="sender">对象</param>
        /// <param name="e">路由参数</param>
        protected virtual void OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            this.Controller.MouseMove -= this.OnMouseMove;
            this.Controller.MouseUp -= this.OnMouseUp;
            this.Controller.ReleaseMouseCapture();
            this.Controller.Cursor = this.OldCursor;
            this.Completed(new ManipulationEventArgs(Mouse.GetPosition(this.Controller)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        protected Point Project(Point3D p)
        {
            return Viewport3DHelper.Point3DtoPoint2D(this.Viewport, p);
        }

        /// <summary>
        /// 设置鼠标按下的2D点
        /// </summary>
        /// <param name="position"></param>
        private void SetMouseDownPoint(Point position)
        {
            this.MouseDownPoint = position;

            Point3D nearestPoint;
            Vector3D normal;
            DependencyObject visual;
            if (Viewport3DHelper.FindNearest(
                this.Controller.Viewport, this.MouseDownPoint, out nearestPoint, out normal, out visual))
            {
                this.MouseDownNearestPoint3D = nearestPoint;
            }
            else
            {
                this.MouseDownNearestPoint3D = null;
            }

            this.MouseDownPoint3D = this.UnProject(this.MouseDownPoint);
        }

        #endregion
    }
}
